// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/resources/sharedsource/licensingbasics/sharedsourcelicenses.mspx.
// All other rights reserved.

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Collections;
using System.Configuration;
using System.Reflection;
using System.Resources;
using System.Security.Principal;
using System.ComponentModel;
using System.Web.Services;

using QuoteAdvantageEditor.CrmSdk;

namespace QuoteAdvantageEditor
{
	/// <summary>
	/// Simplfied version of the QuoteAdvantage Engine for the editor tool 
	/// </summary>
	public class AuthoringEngine
	{
      string                  m_strWebServiceUrl;      
      CrmService              m_objService = null;
      ICredentials            m_objCredentials = null;
      WhoAmIResponse          m_objUserIdentity = null;                 
      
      Hashtable               m_objProductCache = new Hashtable(100);    // Products cached by Product ID
      Hashtable               m_objUnitOfMeasureCache = new Hashtable(50);

		public AuthoringEngine()
		{
		}
      
     
      public void Connect(Settings objSettings)
      {
         DebugMessage("Engine.Connect()");

         string strUserName = "";
         string strPassword = "";
         string strServerName = "";

         try
         {
            // Get current user name without the domain name at the beginning.
            string         strCurrentWindowsUser = WindowsIdentity.GetCurrent().Name;
            int nSlashPos = strCurrentWindowsUser.IndexOf("\\");                   
            if (nSlashPos != -1)
               strCurrentWindowsUser = strCurrentWindowsUser.Substring(nSlashPos+1, strCurrentWindowsUser.Length-nSlashPos-1);
            
            if (objSettings.strCrmSdkUrl == null)
            {
               throw new ApplicationException("Microsoft CRM Server name not specified.");
            }

            strUserName = objSettings.strCrmUserName;
            strPassword = objSettings.strCrmPassword;
            m_strWebServiceUrl = objSettings.strCrmSdkUrl;
            DebugMessage("Connecting to: " + m_strWebServiceUrl);

            m_objService = new CrmService();
            m_objService.Url = m_strWebServiceUrl;

            if (strUserName == "" || strUserName == null)
            {
               strUserName = strCurrentWindowsUser;
               m_objCredentials = System.Net.CredentialCache.DefaultCredentials;
            }
            else
            {
               string strDomain = "";
               // Instantiate a System.Net.CredentialCache object.
               System.Net.CredentialCache credentialCache = new System.Net.CredentialCache();

               // Instantiate a System.Net.NetworkCredential object passing in the Active Directory UserName,
               // Password and Domain that will be used to authenticate into the Microsoft CRM Security Model.
               // Set up this user in Microsoft CRM and give the user the appropriate License Keys and Roles.
               System.Net.NetworkCredential credentials =   new System.Net.NetworkCredential (
                  strUserName,
                  strPassword, 
                  strDomain);
         
               // Add a value to the CredentialCache to indicate the URI of the Microsoft CRM Server, "NTLM",
               // which represents integrated Windows authenticated and the credentials of
               // the user who is to be used for authentication.
               Uri objUri = new Uri(m_strWebServiceUrl);
               strServerName = objUri.GetLeftPart(UriPartial.Authority);
               credentialCache.Add(new Uri(strServerName), "NTLM", credentials);
               //   string uri = "http://" + m_objSettings.ServerName; 
               //   credentialCache.Add(new Uri(uri), "NTLM", credentials);
               // m_objBizUser.Credentials = credentialCache; 
               m_objCredentials = credentialCache;
            }
            DebugMessage("CRM Login Name = " + strUserName);
         
            m_objService.Credentials = m_objCredentials;
            WhoAmIRequest objRequest = new WhoAmIRequest();
            m_objUserIdentity = (WhoAmIResponse)m_objService.Execute(objRequest);
         }
         catch (Exception ex)
         {
            DebugMessage("Throwing engine exception...");
            throw new ProductConfigException(ex.Message + "\r\n\r\nUrl: " + m_strWebServiceUrl + "\r\nUserName: " + strUserName, ex); 
         }
      }

     
      public bool IsConnected()
      {
         bool bResult = false;

         if (m_objUserIdentity != null)
            bResult = true;

         return bResult;
      }


      public XmlNode LoadChildProductCategoryList(string strRuleFileName)
      {
         DebugMessage("LoadChildProductCategoryList() 1");

         XmlNode     nodeRoot = null;     
         XmlDocument    docXml = new XmlDocument();

         docXml.Load(strRuleFileName);
         nodeRoot = docXml.DocumentElement;
         
         DebugMessage("LoadChildProductCategoryList() 2");

         return nodeRoot;                 
      }

      public Organization LoadOrganization()
      {
         DebugMessage("LoadOrganization()");
         Organization  objOrg = null;
         
         TargetRetrieveBusinessUnit targetGetBusinessUnit = new TargetRetrieveBusinessUnit();
         targetGetBusinessUnit.EntityId = m_objUserIdentity.BusinessUnitId;
         RetrieveRequest getBusinessUnit = new RetrieveRequest();
         getBusinessUnit.Target = targetGetBusinessUnit;
         getBusinessUnit.ColumnSet = new AllColumns();
         RetrieveResponse businessUnitResponse = (RetrieveResponse)m_objService.Execute(getBusinessUnit);
         businessunit objBusinessUnit = (businessunit)businessUnitResponse.BusinessEntity;
         
         ColumnSet objColumns = new ColumnSet();
         objColumns.Attributes = new string[] {"currencyformatcode", "currencysymbol", "organizationid"};
         organization objCrmOrganization = (organization)m_objService.Retrieve(EntityName.organization.ToString(), objBusinessUnit.organizationid.Value, objColumns);
         objOrg = new Organization();
         objOrg.LoadFromCrmOrganization(objCrmOrganization);            

         return objOrg;     
      }
         
      public XmlNode LoadProductCategoryList(string strFileName)
      {
         DebugMessage("LoadProductCategoryList()");
         XmlDocument    docXml = new XmlDocument();

         docXml.Load(strFileName);
         XmlNode     nodeRoot = docXml.DocumentElement;
         XmlNode     nodeProductCategory = null;
         
         foreach(XmlNode node in nodeRoot.ChildNodes)
         {
            if (node.Name == "CategoryList")
            {
               nodeProductCategory = node;
            }
         }

         return nodeProductCategory;
      }
       
      public ArrayList LoadCategoryItems(string strCategoryName, XmlNode nodeCategoryListRoot)
      {
         ArrayList      resultList = new ArrayList();
         XmlDocument    docXml = nodeCategoryListRoot.OwnerDocument;
         XmlNode        nodeCategory = docXml.SelectSingleNode("//Category[@categoryId='" + strCategoryName + "']");

         if (nodeCategory != null)
         {
            foreach (XmlNode node in nodeCategory.ChildNodes)
            {
               if (node.Name == "Item")
               {
                  try
                  {
                     string strProductId = node.Attributes["productId"].Value;

                     Product  objProduct = LoadProductFromProductNumber(strProductId);
               
                     if (objProduct != null)
                        resultList.Add(objProduct);
                  }
                  catch (Exception)
                  {
                     throw new ApplicationException("Error processing CategoryList.xml file. 'productId' attribute was not found.");
                  }
               }
            } // end for
         }

         return resultList;
      }
      
      public Product LoadProductFromProductNumber(string strProductNumber)
      {   
         DebugMessage("LoadProductProductNumber('" + strProductNumber + "')");
         Product  objProduct = null;
         
         objProduct = (Product)m_objProductCache[strProductNumber.ToUpper()];

         if (objProduct == null)
         {
            ColumnSet objColumns = new ColumnSet();
            objColumns.Attributes = new string[] {"defaultuomid", "description", "iskit", "name", "price", "productid", "productnumber", "standardcost", "defaultuomscheduleid"};
                    
            // Create a ConditionExpression.
            ConditionExpression conditionPrincipal = new ConditionExpression();

            // Set the ConditionExpressions properties so that the condition is
            // true when the ownerid of the account equals the principalId.
            conditionPrincipal.AttributeName = "productnumber";
            conditionPrincipal.Operator = ConditionOperator.Equal;
            conditionPrincipal.Values = new object [1];
            conditionPrincipal.Values[0] = strProductNumber;
            
            // Create the FilterExpression.
            FilterExpression filterPrincipal = new FilterExpression();

            // Set the properties of the FilterExpression.
            filterPrincipal.FilterOperator = LogicalOperator.And;
            filterPrincipal.Conditions = new ConditionExpression[] {conditionPrincipal};

            // Create the QueryExpression.
            QueryExpression queryPrincipal = new QueryExpression();

            // Set the properties of the QueryExpression.
            queryPrincipal.EntityName = EntityName.product.ToString();
            queryPrincipal.ColumnSet = objColumns;
            queryPrincipal.Criteria = filterPrincipal;

            BusinessEntityCollection objList = m_objService.RetrieveMultiple(queryPrincipal);
            
            if (objList.BusinessEntities.Length > 0)
            {
               objProduct = new Product();
               objProduct.LoadFromCrmProduct((product)objList.BusinessEntities[0]);
            }

            if (objProduct != null)
            {
               m_objProductCache.Add(objProduct.ProductNumber.ToUpper(), objProduct);
            }
         }
         else
         {
            // The product was found in the cache. We need to return a clone of it.
            objProduct = objProduct.Clone();
            DebugMessage("   Product ID " + strProductNumber + " retrieved from cache.");
         }

         return objProduct;
      }
      
     
      public ArrayList LoadProductNameLike(string strProductName)
      {   
         DebugMessage("LoadProductNameLike('" + strProductName + "')");
         Product  objProduct = null;
         
         ColumnSet objColumns = new ColumnSet();
         objColumns.Attributes = new string[] {"defaultuomid", "description", "iskit", "name", "price", "productid", "productnumber", "standardcost", "defaultuomscheduleid"};
                  
         // Create a ConditionExpression.
         ConditionExpression conditionPrincipal = new ConditionExpression();

         // Set the ConditionExpressions properties so that the condition is
         // true when the ownerid of the account equals the principalId.
         conditionPrincipal.AttributeName = "name";
         conditionPrincipal.Operator = ConditionOperator.Like;
         conditionPrincipal.Values = new object [1];
         conditionPrincipal.Values[0] = "%" + strProductName + "%";
         
         // Create the FilterExpression.
         FilterExpression filterPrincipal = new FilterExpression();

         // Set the properties of the FilterExpression.
         filterPrincipal.FilterOperator = LogicalOperator.And;
         filterPrincipal.Conditions = new ConditionExpression[] {conditionPrincipal};

         // Create the QueryExpression.
         QueryExpression queryPrincipal = new QueryExpression();

         // Set the properties of the QueryExpression.
         queryPrincipal.EntityName = EntityName.product.ToString();
         queryPrincipal.ColumnSet = objColumns;
         queryPrincipal.Criteria = filterPrincipal;

         BusinessEntityCollection objList = m_objService.RetrieveMultiple(queryPrincipal);
         
         ArrayList objResultList = new ArrayList(objList.BusinessEntities.Length);
         for (int i = 0; i < objList.BusinessEntities.Length; i++)
         {
            objProduct = new Product();
            objProduct.LoadFromCrmProduct((product)objList.BusinessEntities[i]);
            objResultList.Add(objProduct);
         }

         return objResultList;
      }


      public ArrayList LoadProductNumberLike(string strProductNumber)
      {   
         DebugMessage("LoadProductNumberLike('" + strProductNumber + "')");
         Product  objProduct = null;
         
         ColumnSet objColumns = new ColumnSet();
         objColumns.Attributes = new string[] {"defaultuomid", "description", "iskit", "name", "price", "productid", "productnumber", "standardcost", "defaultuomscheduleid"};
                  
         // Create a ConditionExpression.
         ConditionExpression conditionPrincipal = new ConditionExpression();

         // Set the ConditionExpressions properties so that the condition is
         // true when the ownerid of the account equals the principalId.
         conditionPrincipal.AttributeName = "productnumber";
         conditionPrincipal.Operator = ConditionOperator.Like;
         conditionPrincipal.Values = new object [1];
         conditionPrincipal.Values[0] = "%" + strProductNumber + "%";
         
         // Create the FilterExpression.
         FilterExpression filterPrincipal = new FilterExpression();

         // Set the properties of the FilterExpression.
         filterPrincipal.FilterOperator = LogicalOperator.And;
         filterPrincipal.Conditions = new ConditionExpression[] {conditionPrincipal};

         // Create the QueryExpression.
         QueryExpression queryPrincipal = new QueryExpression();

         // Set the properties of the QueryExpression.
         queryPrincipal.EntityName = EntityName.product.ToString();
         queryPrincipal.ColumnSet = objColumns;
         queryPrincipal.Criteria = filterPrincipal;

         BusinessEntityCollection objList = m_objService.RetrieveMultiple(queryPrincipal);
         
         ArrayList objResultList = new ArrayList(objList.BusinessEntities.Length);
         for (int i = 0; i < objList.BusinessEntities.Length; i++)
         {
            objProduct = new Product();
            objProduct.LoadFromCrmProduct((product)objList.BusinessEntities[i]);
            objResultList.Add(objProduct);
         }

         return objResultList;
      }
          

      public string GetRuleFileName(string strCategoryId, string strProductNumber, XmlNode nodeCategoryListRoot)
      {
         DebugMessage("GetRuleFileName() 1");

         string         strRuleFileName = "";

         XmlNode  nodeCategory = nodeCategoryListRoot.SelectSingleNode("//Category[@categoryId='" + strCategoryId + "']");

         if (nodeCategory != null)
         {
            XmlNode  nodeItem = nodeCategory.SelectSingleNode("//Item[@productId='" + strProductNumber + "']");
         
            if (nodeItem != null)
            {
               XmlAttribute   attr = nodeItem.Attributes["ruleFileName"];

               if (attr != null)
               {
                  strRuleFileName = attr.Value;
               }
            }
         }
         
         DebugMessage("GetRuleFileName() 2");

         return strRuleFileName;                 
      }  

      public string LoadUnitOfMeasure(Guid unitOfMeasureId, Organization objOrganization)
      {
         string strResult = "";

         if (unitOfMeasureId != Guid.Empty)
         {
            uom objUom = (uom)m_objUnitOfMeasureCache[unitOfMeasureId];

            if (objUom == null)
            {
               // Load the unit of measure table from the database
               ColumnSet objColumns = new ColumnSet();
               objColumns.Attributes = new string[] {"uomid", "uomscheduleid", "name", "quantity"};
            
               // Create a ConditionExpression.
               ConditionExpression conditionPrincipal1 = new ConditionExpression();

               conditionPrincipal1.AttributeName = "organizationid";
               conditionPrincipal1.Operator = ConditionOperator.Equal;
               conditionPrincipal1.Values = new object [1];
               conditionPrincipal1.Values[0] = objOrganization.OrganizationId;
             
               // Create the FilterExpression.
               FilterExpression filterPrincipal = new FilterExpression();

               // Set the properties of the FilterExpression.
               filterPrincipal.FilterOperator = LogicalOperator.And;
               filterPrincipal.Conditions = new ConditionExpression[] {conditionPrincipal1};

               // Create the QueryExpression.
               QueryExpression queryPrincipal = new QueryExpression();

               // Set the properties of the QueryExpression.
               queryPrincipal.EntityName = EntityName.uom.ToString();
               queryPrincipal.ColumnSet = objColumns;
               queryPrincipal.Criteria = filterPrincipal;

               BusinessEntityCollection objList = m_objService.RetrieveMultiple(queryPrincipal);
            
               m_objUnitOfMeasureCache.Clear();

               if (objList.BusinessEntities.Length > 0)
               {
                  foreach (BusinessEntity objItem in objList.BusinessEntities)
                  {
                     uom objUnit = (uom)objItem;
                     m_objUnitOfMeasureCache.Add(objUnit.uomid.Value, objUnit);
                  }
               } 
            
               objUom = (uom)m_objUnitOfMeasureCache[unitOfMeasureId];
            }

            if (objUom != null)
            {
               strResult = objUom.name;
            }
         }

         return strResult;
      }

      public ArrayList LoadUnitOfMeasureList(Product objProduct)
      {   
         DebugMessage("LoadUnitOfMeasureList('" + objProduct.ProductNumber + "')");
         
         ColumnSet objColumns = new ColumnSet();
         objColumns.Attributes = new string[] {"uomid", "uomscheduleid", "name", "quantity"};
                  
         // Create a ConditionExpression.
         ConditionExpression conditionPrincipal = new ConditionExpression();

         // Set the ConditionExpressions properties so that the condition is
         // true when the ownerid of the account equals the principalId.
         conditionPrincipal.AttributeName = "uomscheduleid";
         conditionPrincipal.Operator = ConditionOperator.Equal;
         conditionPrincipal.Values = new object [1];
         conditionPrincipal.Values[0] = objProduct.DefaultUomScheduleId;
         
         // Create the FilterExpression.
         FilterExpression filterPrincipal = new FilterExpression();

         // Set the properties of the FilterExpression.
         filterPrincipal.FilterOperator = LogicalOperator.And;
         filterPrincipal.Conditions = new ConditionExpression[] {conditionPrincipal};

         // Create the QueryExpression.
         QueryExpression queryPrincipal = new QueryExpression();

         // Set the properties of the QueryExpression.
         queryPrincipal.EntityName = EntityName.uom.ToString();
         queryPrincipal.ColumnSet = objColumns;
         queryPrincipal.Criteria = filterPrincipal;

         BusinessEntityCollection objList = m_objService.RetrieveMultiple(queryPrincipal);
         
         ArrayList objResultList = new ArrayList(objList.BusinessEntities.Length);
         
         foreach(uom objUnit in objList.BusinessEntities)
         {
            objResultList.Add(objUnit);
         }

         return objResultList;
      }
   
      void DebugMessage(string strMessage)
      {
         DateTime timeStamp = DateTime.Now;
         string   strTemp = timeStamp.ToShortTimeString() + " " + timeStamp.Second + ":" + timeStamp.Millisecond + " sec - " + strMessage + "\r\n";

         Debug.Write(strTemp);
      }

    
      // Map a category ID to the name of a category in the rule file
      private string GetCategoryName(string strCategoryId, XmlNode nodeRoot)
      {
         string strCategoryName = "";

         foreach (XmlNode nodeCategory in nodeRoot.ChildNodes)
         {
            if (nodeCategory.Attributes["categoryId"].Value == strCategoryId)
            {
               strCategoryName = nodeCategory.Attributes["name"].Value;
            }
         }

         return strCategoryName;
      }
      

	} // end class
}
